package nonDictionaryTokenAnalyzer;

import inporter.DictionaryImporter;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Hashtable;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import main.tokens;

public class MainProcessor 
{
	//private ArrayList<String> arlCodeFile;
	private ArrayList<tokens> arlTokenArray;
	//hash table for storing tokens which share same alphabet
	private Hashtable<String, ArrayList<tokens>> htAlphaTokens;
	//ArrayLsit for storing hash table keys
	private ArrayList<String> arlHashTableKeys;
	
	private DictionaryImporter clsDImporter;
	private AbbreviationProcessor clsAProcessor;
	private SpitTokenProcessor clsSTProcessor;
	
	//Hash table which contains dictionary words
	Hashtable<String, String> htDicWords;
	
	/*
	 * Function: Construction function, Initial every thing
	 */
	public MainProcessor(ArrayList<String> pCodeFile, ArrayList<tokens> pTokenArray)
	{
		//arlCodeFile = pCodeFile;
		arlTokenArray = pTokenArray;
		clsAProcessor = new AbbreviationProcessor(pCodeFile);
		
		//get tokens from dictionary
		clsDImporter = new DictionaryImporter("dictionary");
		htDicWords = clsDImporter.getDicTokenArray();
		
		//hash table for storing tokens which share same alphabet
		htAlphaTokens = new Hashtable<String, ArrayList<tokens>>();
		arlHashTableKeys = new ArrayList<String>();		
	}
	
	/*
	 * Function: Main processor of the class, scan the non-dictionary token, divide them by length, and process
	 * Input: None
	 * Output: arlHashTableKeys -> Array list which contains hash table keys (use getHashTble function to get the result)
	 */
	public ArrayList<String> nonDicTokenProcess()
	{
		String strCntToken = "";
		
		String s = "[a-z]+";
		Pattern pattern=Pattern.compile(s);
       
		for (int i = 0; i < arlTokenArray.size(); i ++)
		{
			 Matcher ma=pattern.matcher(arlTokenArray.get(i).getToken().toLowerCase());
		     if (ma.find())
		     {
		    	 strCntToken = ma.group();
		     }
		     		     
		     if (strCntToken.length() == 1)
		     {
		    	 // token length = 1, so abandon the token by setting state to -1
		    	 arlTokenArray.get(i).setState(-1);
		     }
		     else if(strCntToken.length() == 2) 
		     {
		    	 // token length = 2, so do the Abbreviation process
		    	 String strRtn = clsAProcessor.getProcessorResult(strCntToken, arlTokenArray.get(i));
		    	 if (!strRtn.trim().equals(""))
		    	 {
		    		 arlTokenArray.get(i).setResult(strRtn);
		    		 InsertIntoHash(strCntToken,arlTokenArray.get(i));
		    	 }
		    	 // put current token into hash table
		     }
		     else
		     {
		    	 // token length > 2, put token into hash table first
		    	 InsertIntoHash(strCntToken,arlTokenArray.get(i));
		     }
		}
		
		// Sort the Hash table key array by length and alphabet order
		arlHashTableKeys = ArrayListSortByLength(arlHashTableKeys);
		
		// Get the best result for Abbreviations
		getBestAbbreviationResult();
		
		// All tokens has been divided into 3 groups by length, and stored into hash table
		clsSTProcessor = new SpitTokenProcessor(htDicWords, htAlphaTokens);
		for (int i = 0; i < arlHashTableKeys.size(); i++)
		{
			// Scan Hash table key, if key's length longer than 2, throw it to splitting processor
			String strCntTokenKey = arlHashTableKeys.get(i);
			if (strCntTokenKey.length() > 2 && strCntTokenKey.length() < 27)
			{
				String strResult = clsSTProcessor.getProcessorResult(strCntTokenKey);
				if (!strResult.equals(""))
				{
					setProcessResult(strCntTokenKey, strResult);
				}
				//System.out.println(arlHashTableKeys.get(i) + ": " + strResult);
			}
		}
		
//		String strResult = clsSTProcessor.getProcessorResult("beanoperatorepuntodiristoro");
//		System.out.println(strResult);
//		System.out.println();
		
		return arlHashTableKeys;
	}
	
	public Hashtable<String, ArrayList<tokens>> getHashTable()
	{
		 return htAlphaTokens;
	}
	
	/*
	 * Function: Insert tokens into hash table, and store keys into the other ArrayList
	 * Input: pAlphaToken -> alphabet token, use as key in hash table
	 * 		  pCntToken -> token which needs to be input into hash table
	 */
	private void InsertIntoHash(String pAlphaToken, tokens pCntToken)
	{
		// Get tokens' array list with current key
		ArrayList<tokens> arlTmpTokens = htAlphaTokens.get(pAlphaToken);
		
		if (arlTmpTokens == null)
		{
			// Hash table does not contain the current key
			arlTmpTokens = new ArrayList<tokens>();
			arlTmpTokens.add(pCntToken);
			// Add current token to hash table
			htAlphaTokens.put(pAlphaToken, arlTmpTokens);
			// Add key to key array
			arlHashTableKeys.add(pAlphaToken);
		}
		else
		{
			// Hash table contains the current key
			arlTmpTokens.add(pCntToken);
			// Replace hash table element
			htAlphaTokens.remove(pAlphaToken);
			htAlphaTokens.put(pAlphaToken, arlTmpTokens);
		}
	}
	
	/*
	 * Function: Sort the array list by length and alphabet order
	 * Input: pBefore -> array list before sort
	 * Output: arlAfter -> array list after sort
	 */
	private ArrayList<String> ArrayListSortByLength(ArrayList<String> pBefore)
	{
		ArrayList<String> arlAfter = new ArrayList<String>();
		Hashtable<Integer, ArrayList<String>> htBig = new Hashtable<Integer, ArrayList<String>>();
		int intLongest = 0;
		ArrayList<String> arlTmp;
		
		for (int i =0; i < pBefore.size(); i++)
		{
			int intKeyLength = pBefore.get(i).length();
			if (htBig.get(intKeyLength) == null)
			{
				//First key at this length
				arlTmp = new ArrayList<String>();
				arlTmp.add(pBefore.get(i));
				
				//Add new array list to hash table
				htBig.put(intKeyLength, arlTmp);
			}
			else
			{
				//token with same length existed
				arlTmp = htBig.get(intKeyLength);
				arlTmp.add(pBefore.get(i));
				
				//Update the hash table info
				htBig.remove(intKeyLength);
				htBig.put(intKeyLength, arlTmp);
			}
			
			if (intKeyLength > intLongest)
			{
				// longest token's length update
				intLongest = intKeyLength;
			}
		}
		
		for (int i = 1; i <= intLongest; i ++)
		{
			if (htBig.get(i) != null)
			{
				Collections.sort(htBig.get(i));
				for (int j = 0; j < htBig.get(i).size(); j ++)
				{
					arlAfter.add(htBig.get(i).get(j));
				}
			}
		}		
		return arlAfter;	
	}
	
	/*
	 * Function: Get best abbreviation result from all candidates
	 * Input: None
	 * Output: None
	 */
	private void getBestAbbreviationResult()
	{
		int intCount = 0;
		String strCntKey = arlHashTableKeys.get(intCount);
		
		while(strCntKey.length() == 2)
		{
			//System.out.println(strCntKey);
			ArrayList<tokens> arlTokensWithSameKey = htAlphaTokens.get(strCntKey);
			// ArrayList to contain the current results
			ArrayList<String> arlCntTmpResult = new ArrayList<String>();
			
			// Collect all candidates from tokens which have same key
			String[] strsCandidates;
			String strCntBestResult = "";
			
			for (int i = 0; i < arlTokensWithSameKey.size(); i ++)
			{
				if (arlTokensWithSameKey.get(i).getResult().contains(", "))
				{
					// The result contains multiple candidates
					strsCandidates = arlTokensWithSameKey.get(i).getResult().split(", ");
				}
				else
				{
					// The result only have one candidate
					strsCandidates = new String[]{arlTokensWithSameKey.get(i).getResult()};
				}
				
				for (int j = 0; j < strsCandidates.length; j++)
				{
					if (!arlCntTmpResult.contains(strsCandidates[j]))
					{
						if (bolIsDictionary(strsCandidates[j].trim()))
						{
							arlCntTmpResult.add(strsCandidates[j].trim());
						}
					}
					
				}
								
			}
			
			// combine the results together
			if (arlCntTmpResult.size() != 0)
			{
				for (int j = 0; j < arlCntTmpResult.size(); j ++)
				{
					strCntBestResult = strCntBestResult + arlCntTmpResult.get(j) + ", ";
				}
				strCntBestResult = strCntBestResult.substring(0, strCntBestResult.length() - 2);
			}
			
			// Set result to each token
			for (int i = 0; i < htAlphaTokens.get(strCntKey).size(); i ++)
			{
				htAlphaTokens.get(strCntKey).get(i).setResult(strCntBestResult);
			}
			
			// Prepare for next token key
			intCount ++;
			strCntKey = arlHashTableKeys.get(intCount);
		}
	}
	
	/*
	 * Function: Check the current result is dictionary word or not
	 * Input: pTmp -> Result waiting to check
	 * Output: bolRtn -> Checking result
	 */
	private Boolean bolIsDictionary(String pTmp)
	{
		Boolean bolRtn = true;
		if (pTmp.contains(" "))
		{
			String[] strsTmp = pTmp.split((" "));
			for (int i = 0; i < strsTmp.length; i ++)
			{
				if (!htDicWords.containsKey(strsTmp[i]))
				{
					bolRtn = false;
				}
			}
		}
		else
		{
			if (!htDicWords.containsKey(pTmp))
			{
				bolRtn = false;
			}
		}
		
		return bolRtn;
	}
	
	/*
	 * Function: set process result to hash table
	 * Input: pAlphaToken -> token key
	 * 		  pResult -> process result
	 * Output: None
	 */
	private void setProcessResult(String pAlphaToken, String pResult)
	{
		ArrayList<tokens> arlCntTokens = htAlphaTokens.get(pAlphaToken);
		
		for (int i = 0; i < arlCntTokens.size(); i++)
		{
			arlCntTokens.get(i).setResult(pResult);
		}
		
		htAlphaTokens.remove(pAlphaToken);
		htAlphaTokens.put(pAlphaToken, arlCntTokens);
	}
}
